﻿using System;
using System.Collections.Generic;

using Blog.TemplateEngine.Helpers;
using Blog.TemplateEngine.Nodes;

namespace Blog.TemplateEngine
{
    public class Parser
    {
        Engine mEngine;
        List<Token> mTokens;

        public Parser(List<Token> tokens, Engine engine)
        {
            mEngine = engine;
            mTokens = tokens;
        }

        public NodeList Parse()
        {
            return Parse(new string[] {});
        }

        public NodeList Parse(string[] endTags)
        {
            var nodes = new NodeList();

            while (!mTokens.IsEmpty())
            {
                var token = mTokens.Pop();

                switch (token.Type)
                {
                    case TokenType.Text:
                        nodes.Add(new TextNode(token.Content));
                        break;
                    case TokenType.Variable:
                        nodes.Add(CompileVariable(token));
                        break;
                    case TokenType.Block:
                        if(endTags.Contains(token.Content))
                        {
                            mTokens.Push(token);
                            return nodes;
                        }
                        nodes.Add(CompileBlock(token));
                        break;
                }
            }

            if(endTags.Length != 0)
            {
                throw new TemplateException(String.Format("Reached end of template while looking for possible end tag(s): {0}", endTags.Join(", ")));
            }
            return nodes;
        }

        public Token NextToken()
        {
            return mTokens.Pop();
        }

        public void DeleteNextToken()
        {
            mTokens.Pop();
        }

        protected INode CompileVariable(Token token)
        {
            var source = token.Content.SmartSplitAndTrim('|');

            if(source.Length == 0)
            {
                throw new TemplateException("Variable block with no variable name defined.");
            }
            INode node = new VariableNode(source[0]);

            for (var i = 1; i < source.Length; i++)
            {
                var filterSource = source[i].SmartSplitAndTrim(':');

                IFilter filter = mEngine.FindFilter(filterSource[0]);

                if (filterSource.Length == 1 && !filter.HasParameter)
                {
                    node = new FilterNode(node, filter);
                }
                else if (filterSource.Length == 2 && filter.HasParameter)
                {
                    string parameter = filterSource[1];
                    if (parameter.StartsWith(@"""") && parameter.EndsWith(@""""))
                    {
                        parameter = parameter.Substring(1, parameter.Length - 2);
                    }
                    else
                    {
                        throw new TemplateException("Filter parameters must be wrapped in double quotes: " + parameter);
                    }

                    node = new FilterNode(node, filter, parameter);
                }
                else
                {
                    throw new TemplateException("Too many parameters for filter: " + filterSource[0]);
                }
            }

            return node;
        }

        protected INode CompileBlock(Token token)
        {
            string[] source = token.Content.SmartSplitAndTrim(' ');
            var function = mEngine.FindFunction(source[0]);
            return function.Compile(this, token);
        }
    }
}
